import 'dart:async';
import 'dart:io';

import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:riki_router/core/riki_route_core.dart';
import 'riki_route_helper.dart';

/// Navigator 1.0
class RikiRouteSettings extends RouteSettings {
  const RikiRouteSettings({
    required String name,
    Object? arguments,
    this.widget,
    this.showStatusBar,
    this.routeName,
    this.pageRouteType,
    this.description,
    this.exts,
    this.transitionMilliseconds,
    this.reverseTransitionMilliseconds,
  }) : super(
          name: name,
          arguments: arguments,
        );

  /// The Widget base on this route
  final Widget? widget;

  /// Whether show status bar.
  final bool? showStatusBar;

  /// The route name to track page
  final String? routeName;

  /// The type of page route
  final PageRouteType? pageRouteType;

  /// The description of route
  final String? description;

  /// The extend arguments
  final Map<String, dynamic>? exts;

  /// 转场动画时长
  final int? transitionMilliseconds;

  /// 反向转场时长
  final int? reverseTransitionMilliseconds;

  Route<dynamic> createRoute() {
    switch (pageRouteType) {
      case PageRouteType.material:
        return RikiMaterialPageRoute<dynamic>(
          settings: this,
          transitionDuration: _transition(transitionMilliseconds, kRikiMaterialTransitionDuration),
          reverseTransitionDuration: _transition(reverseTransitionMilliseconds, kRikiMaterialTransitionDuration),
          builder: (BuildContext _) => widget!,
        );
      case PageRouteType.cupertino:
        return RikiCupertinoPageRoute<dynamic>(
          settings: this,
          transitionDuration: _transition(transitionMilliseconds, kRikiCupertinoTransitionDuration),
          reverseTransitionDuration: _transition(reverseTransitionMilliseconds, kRikiCupertinoTransitionDuration),
          builder: (BuildContext _) => widget!,
        );
      case PageRouteType.transparent:
        return RikiTransparentPageRoute<dynamic>(
          settings: this,
          transitionDuration: _transition(transitionMilliseconds, kRikiTransitionDuration),
          reverseTransitionDuration: _transition(reverseTransitionMilliseconds, kRikiTransitionDuration),
          pageBuilder: (
            BuildContext _,
            Animation<double> __,
            Animation<double> ___,
          ) =>
              widget!,
        );
      default:
        return kIsWeb || !Platform.isIOS
            ? RikiMaterialPageRoute<dynamic>(
                settings: this,
                transitionDuration: _transition(transitionMilliseconds, kRikiMaterialTransitionDuration),
                reverseTransitionDuration: _transition(reverseTransitionMilliseconds, kRikiMaterialTransitionDuration),
                builder: (BuildContext _) => widget!,
              )
            : RikiCupertinoPageRoute<dynamic>(
                settings: this,
                transitionDuration: _transition(transitionMilliseconds, kRikiCupertinoTransitionDuration),
                reverseTransitionDuration: _transition(reverseTransitionMilliseconds, kRikiCupertinoTransitionDuration),
                builder: (BuildContext _) => widget!,
              );
    }
  }

  @override
  RikiRouteSettings copyWith({
    String? name,
    Object? arguments,
    Widget? widget,
    bool? showStatusBar,
    String? routeName,
    PageRouteType? pageRouteType,
    String? description,
    Map<String, dynamic>? exts,
    int? transitionMilliseconds,
    int? reverseTransitionMilliseconds,
  }) {
    return RikiRouteSettings(
      name: name ?? this.name!,
      arguments: arguments ?? this.arguments,
      widget: widget ?? this.widget,
      showStatusBar: showStatusBar ?? this.showStatusBar,
      routeName: routeName ?? this.routeName,
      pageRouteType: pageRouteType ?? this.pageRouteType,
      description: description ?? this.description,
      exts: exts ?? this.exts,
      transitionMilliseconds: transitionMilliseconds ?? this.transitionMilliseconds,
      reverseTransitionMilliseconds: reverseTransitionMilliseconds ?? this.reverseTransitionMilliseconds,
    );
  }

  RikiPage<T> toRikiPage<T extends Object?>({
    String? name,
    Object? arguments,
    required LocalKey key,
    Widget? widget,
    bool? showStatusBar,
    String? routeName,
    PageRouteType? pageRouteType,
    String? description,
    Map<String, dynamic>? exts,
    int? transitionMilliseconds,
    int? reverseTransitionMilliseconds,
    RouteTransitionsBuilder? routeTransitionsBuilder,
  }) {
    return RikiPage<T>(
      name: name ?? this.name!,
      arguments: arguments ?? this.arguments,
      key: key,
      widget: widget ?? this.widget!,
      showStatusBar: showStatusBar ?? this.showStatusBar,
      routeName: routeName ?? this.routeName,
      pageRouteType: pageRouteType ?? this.pageRouteType,
      description: description ?? this.description,
      exts: exts ?? this.exts,
      transitionMilliseconds: transitionMilliseconds ?? this.transitionMilliseconds,
      reverseTransitionMilliseconds: reverseTransitionMilliseconds ?? this.reverseTransitionMilliseconds,
      routeTransitionsBuilder: routeTransitionsBuilder,
    );
  }

  Map<String, dynamic> toJson() => <String, dynamic>{
        'name': name,
        'arguments': arguments,
        'showStatusBar': showStatusBar,
        'routeName': routeName,
        'pageRouteType': pageRouteType,
        'description': description,
        'exts': exts,
        'transitionMilliseconds': transitionMilliseconds,
        'reverseTransitionMilliseconds': reverseTransitionMilliseconds,
      };
}

/// Navigator 2.0
class RikiPage<T> extends Page<T> {
  RikiPage({
    required String name,
    required LocalKey key,
    required this.widget,
    Object? arguments,
    this.showStatusBar,
    this.routeName,
    this.pageRouteType,
    this.description,
    this.exts,
    this.transitionMilliseconds,
    this.reverseTransitionMilliseconds,
    this.routeTransitionsBuilder,
  }) : super(
          key: key,
          name: name,
          arguments: arguments,
        );

  /// The Widget base on this route
  final Widget widget;

  /// Whether show status bar.
  final bool? showStatusBar;

  /// The route name to track page
  final String? routeName;

  /// The type of page route
  final PageRouteType? pageRouteType;

  /// The description of route
  final String? description;

  /// The extend arguments
  final Map<String, dynamic>? exts;

  /// hero动画的key
  final List<String> heroKeys = <String>[];

  /// 转场动画时长
  final int? transitionMilliseconds;

  /// 反向转场动画时长
  final int? reverseTransitionMilliseconds;

  /// 自定义过度动画
  final RouteTransitionsBuilder? routeTransitionsBuilder;

  /// A future that completes when this route is popped off the navigator.
  ///
  /// The future completes with the value given to [Navigator.pop], if any, or
  /// else the value of [null].
  Future<T> get popped => _popCompleter.future;
  final Completer<T> _popCompleter = Completer<T>();

  bool get isCompleted => _popCompleter.isCompleted;

  /// pop this route
  bool didPop([T? result]) {
    if (!_popCompleter.isCompleted) {
      _popCompleter.complete(result);
    }
    return true;
  }

  @override
  Route<T> createRoute(BuildContext context) {
    final Route<T> route = _createRoute(context);
    return route
      ..popped.then((T? value) {
        if (!isCompleted) {
          _popCompleter.complete(value);
        }
      });
  }

  Route<T> _createRoute(BuildContext context) {
    if (routeTransitionsBuilder != null) {
      return RikiTransparentPageRoute<T>(
        settings: this,
        transitionsBuilder: routeTransitionsBuilder!,
        transitionDuration: _transition(transitionMilliseconds, kRikiTransitionDuration),
        reverseTransitionDuration: _transition(reverseTransitionMilliseconds, kRikiTransitionDuration),
        pageBuilder: (
          BuildContext _,
          Animation<double> __,
          Animation<double> ___,
        ) =>
            widget,
      );
    }
    switch (pageRouteType) {
      case PageRouteType.material:
        return RikiMaterialPageRoute<T>(
          settings: this,
          transitionDuration: _transition(transitionMilliseconds, kRikiMaterialTransitionDuration),
          reverseTransitionDuration: _transition(reverseTransitionMilliseconds, kRikiMaterialTransitionDuration),
          builder: (BuildContext _) => widget,
        );
      case PageRouteType.cupertino:
        return RikiCupertinoPageRoute<T>(
          settings: this,
          transitionDuration: _transition(transitionMilliseconds, kRikiCupertinoTransitionDuration),
          reverseTransitionDuration: _transition(reverseTransitionMilliseconds, kRikiCupertinoTransitionDuration),
          builder: (BuildContext _) => widget,
        );
      case PageRouteType.transparent:
        return RikiTransparentPageRoute<T>(
          settings: this,
          transitionDuration: _transition(transitionMilliseconds, kRikiTransitionDuration),
          reverseTransitionDuration: _transition(reverseTransitionMilliseconds, kRikiTransitionDuration),
          pageBuilder: (
            BuildContext _,
            Animation<double> __,
            Animation<double> ___,
          ) =>
              widget,
        );
      default:
        return kIsWeb || !Platform.isIOS
            ? RikiMaterialPageRoute<T>(
                settings: this,
                transitionDuration: _transition(transitionMilliseconds, kRikiMaterialTransitionDuration),
                reverseTransitionDuration: _transition(reverseTransitionMilliseconds, kRikiMaterialTransitionDuration),
                builder: (BuildContext _) => widget,
              )
            : RikiCupertinoPageRoute<T>(
                settings: this,
                transitionDuration: _transition(transitionMilliseconds, kRikiCupertinoTransitionDuration),
                reverseTransitionDuration: _transition(reverseTransitionMilliseconds, kRikiCupertinoTransitionDuration),
                builder: (BuildContext _) => widget,
              );
    }
  }

  @override
  RikiPage<T> copyWith({
    String? name,
    Object? arguments,
    LocalKey? key,
    Widget? widget,
    bool? showStatusBar,
    String? routeName,
    PageRouteType? pageRouteType,
    String? description,
    Map<String, dynamic>? exts,
    int? transitionMilliseconds,
    int? reverseTransitionMilliseconds,
    RouteTransitionsBuilder? routeTransitionsBuilder,
  }) {
    return RikiPage<T>(
      name: name ?? this.name!,
      arguments: arguments ?? this.arguments,
      key: key ?? this.key!,
      widget: widget ?? this.widget,
      showStatusBar: showStatusBar ?? this.showStatusBar,
      routeName: routeName ?? this.routeName,
      pageRouteType: pageRouteType ?? this.pageRouteType,
      description: description ?? this.description,
      exts: exts ?? this.exts,
      transitionMilliseconds: transitionMilliseconds ?? this.transitionMilliseconds,
      reverseTransitionMilliseconds: reverseTransitionMilliseconds ?? this.reverseTransitionMilliseconds,
      routeTransitionsBuilder: routeTransitionsBuilder,
    );
  }

  RikiRouteSettings toRikiRouteSettings({
    String? name,
    Object? arguments,
    LocalKey? key,
    Widget? widget,
    bool? showStatusBar,
    String? routeName,
    PageRouteType? pageRouteType,
    String? description,
    Map<String, dynamic>? exts,
    int? transitionMilliseconds,
    int? reverseTransitionMilliseconds,
  }) {
    return RikiRouteSettings(
      name: name ?? this.name!,
      arguments: arguments ?? this.arguments,
      widget: widget ?? this.widget,
      showStatusBar: showStatusBar ?? this.showStatusBar,
      routeName: routeName ?? this.routeName,
      pageRouteType: pageRouteType ?? this.pageRouteType,
      description: description ?? this.description,
      exts: exts ?? this.exts,
      transitionMilliseconds: transitionMilliseconds ?? this.transitionMilliseconds,
      reverseTransitionMilliseconds: reverseTransitionMilliseconds ?? this.reverseTransitionMilliseconds,
    );
  }

  Map<String, dynamic> toJson() => <String, dynamic>{
        'name': name,
        'arguments': arguments,
        'key': key,
        'showStatusBar': showStatusBar,
        'routeName': routeName,
        'pageRouteType': pageRouteType,
        'description': description,
        'exts': exts,
        'transitionMilliseconds': transitionMilliseconds,
        'reverseTransitionMilliseconds': reverseTransitionMilliseconds,
      };
}

Duration _transition(int? milliseconds, Duration defaultDuration) {
  if (milliseconds != null) {
    return Duration(milliseconds: milliseconds);
  }
  return defaultDuration;
}
